package ru.usu.gv.uicomponents;

import ru.usu.gv.core.GraphEngine;
import ru.usu.gv.graph.Graph;
import ru.usu.gv.graph.Vertex;
import ru.usu.gv.styles.StyleFacade;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 @author: Sergey Pupyrev 
 Apr 2, 2006
 */

public class UIGraph
{
	private String id;
	private Graph graph;
	private List<UIVertex> subVertexes = new ArrayList();
	private List<UIEdge> subEdges = new ArrayList();
	private Map<UIVertex, Set<UIEdge>> edges = new HashMap();

	public UIGraph(String id)
	{
		this.id = id;
		graph = new Graph();
	}

	public Graph getGraph()
	{
		return graph;
	}

	public List<UIEdge> getEdges()
	{
		return subEdges;
	}

	public List<UIEdge> getEdges(UIVertex v)
	{
		return new ArrayList(edges.get(v));
	}

	public List<UIVertex> getVertexes()
	{
		return subVertexes;
	}

	public UIVertex getVertex(int index)
	{
		return subVertexes.get(index);
	}

	public void addVertex(UIVertex v)
	{
		Vertex vertex = new Vertex();
		v.setVertex(vertex);

		subVertexes.add(v);
		edges.put(v, new HashSet());

		GraphEngine.getInstance().getUIGraphListeners().doChange(this);
		graph.addVertex(vertex);
	}

	public void removeVertex(UIVertex v)
	{
		Set<UIEdge> list = new HashSet(edges.get(v));
		for (UIEdge edge : list)
			edge.delete();

		subVertexes.remove(v);
		edges.remove(v);

		GraphEngine.getInstance().getUIGraphListeners().doChange(this);
		graph.removeVertex(v.getVertex());
	}

	public void addEdge(UIEdge e)
	{
		if (StyleFacade.isDirectedGraph())
		{
			//1. if exists v1-v2 then return;
			if (existsEdge(e.getV1(), e.getV2()))
				return;
			//2. add ui v1-v2
			subEdges.add(e);
			//3. add edges v1
			edges.get(e.getV1()).add(e);
			//4. add v1-v2
			GraphEngine.getInstance().getUIGraphListeners().doChange(this);
			graph.addEdge(e.getV1().getVertex(), e.getV2().getVertex());
		}
		else
		{
			//1. if exists v1-v2 or v2-v1 then return;
			if (existsEdge(e.getV1(), e.getV2()) || existsEdge(e.getV2(), e.getV1()))
				return;
			//2. add ui v1-v2
			subEdges.add(e);
			//3. add edges v1 & v2
			edges.get(e.getV1()).add(e);
			edges.get(e.getV2()).add(e);
			//4. add v1-v2 & v2-v1
			GraphEngine.getInstance().getUIGraphListeners().doChange(this);
			graph.addEdge(e.getV1().getVertex(), e.getV2().getVertex());
			graph.addEdge(e.getV2().getVertex(), e.getV1().getVertex());
		}
	}

	public void removeEdge(UIEdge e)
	{
		if (StyleFacade.isDirectedGraph())
		{
			subEdges.remove(e);
			edges.get(e.getV1()).remove(e);
			GraphEngine.getInstance().getUIGraphListeners().doChange(this);
			graph.removeEdge(e.getV1().getVertex(), e.getV2().getVertex());
		}
		else
		{
			subEdges.remove(e);
			edges.get(e.getV1()).remove(e);
			edges.get(e.getV2()).remove(e);
			GraphEngine.getInstance().getUIGraphListeners().doChange(this);
			graph.removeEdge(e.getV1().getVertex(), e.getV2().getVertex());
			graph.removeEdge(e.getV2().getVertex(), e.getV1().getVertex());
		}
	}

	public synchronized void draw()
	{
		long startTime = System.currentTimeMillis();
		System.out.print("Drawing graph (nodes=" + subVertexes.size() + ", edges=" + subEdges.size() + ")...");

		boolean isDirected = StyleFacade.isDirectedGraph();
		if (subVertexes.size() <= StyleFacade.getMaxCountVertex())
		{
			for (UIVertex vertex : subVertexes)
			{
				vertex.draw();
				if (subEdges.size() <= StyleFacade.getMaxCountEdges())
				{
					for (UIEdge edge : getEdges(vertex))
					{
						if (isDirected || vertex.equals(edge.getV1()))
							edge.draw();
					}
				}
			}
		}
		else
		{
			System.out.print("skipping...");
		}

		long endTime = System.currentTimeMillis();
		System.out.println("done (" + (endTime - startTime) / 1000.0 + " sec.)");
	}

	private boolean existsEdge(UIVertex v1, UIVertex v2)
	{
		//TODO: faster!!!
		if (edges.get(v1) != null)
		{
			for (UIEdge edge : edges.get(v1))
				if (edge.getV1().equals(v1) && edge.getV2().equals(v2))
					return true;
		}

		return false;
	}

	public String getId()
	{
		return id;
	}

}
